//===================================================================
// 项目名 : Hbt.Cur
// 文件名 : HbtIpUtils.cs
// 创建者 : Claude
// 创建时间: 2024-12-01 14:30
// 版本号 : V0.0.1
// 框架版本: .NET 8.0
// 描述   : IP地址工具类 - 合并IP位置查询和本机IP获取功能
//===================================================================

using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using IP2Region.Net.XDB;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;

namespace Hbt.Common.Utils;

/// <summary>
/// IP地址工具类
/// </summary>
/// <remarks>
/// 提供完整的IP地址处理功能：
/// 1. 客户端IP地址获取（支持代理环境）
/// 2. IP位置查询（使用IP2Region数据库）
/// 3. 本机内网IP地址获取
/// 4. 网络信息获取
/// 5. 支持IPv4和IPv6地址
/// </remarks>
public static class HbtIpUtils
{
    #region IP位置查询相关

    private static Searcher? _searcher;
    private static readonly Regex _ipv4Regex = new(@"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", RegexOptions.Compiled);
    private static readonly Regex _ipv6Regex = new(@"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$", RegexOptions.Compiled);
    private static IWebHostEnvironment? _webHostEnvironment;

    /// <summary>
    /// 设置Web环境
    /// </summary>
    /// <param name="webHostEnvironment">Web主机环境</param>
    public static void SetWebHostEnvironment(IWebHostEnvironment webHostEnvironment)
    {
        try
        {
            _webHostEnvironment = webHostEnvironment;
            var dbPath = Path.Combine(_webHostEnvironment.WebRootPath, "IpRegion", "ip2region.xdb");
            Debug.WriteLine($"数据库文件路径: {dbPath}");
            
            if (!File.Exists(dbPath))
            {
                var error = $"IP2Region数据库文件不存在: {dbPath}";
                Debug.WriteLine(error);
                throw new FileNotFoundException(error);
            }
            
            _searcher = new Searcher(CachePolicy.File, dbPath);
        }
        catch (Exception ex)
        {
            var error = $"初始化IP2Region失败: {ex.GetType().Name} - {ex.Message}\n{ex.StackTrace}";
            Debug.WriteLine(error);
            throw new InvalidOperationException(error, ex);
        }
    }

    /// <summary>
    /// 获取IP地址的位置信息
    /// </summary>
    /// <param name="ipAddress">IP地址</param>
    /// <returns>位置信息，格式：国家|区域|省份|城市|ISP</returns>
    /// <remarks>
    /// 使用IP2Region库查询IP地址的地理位置信息
    /// 如果是内网IP或查询失败则返回特定说明
    /// </remarks>
    public static async Task<string> GetLocationAsync(string ipAddress)
    {
        if (_searcher == null)
        {
            throw new InvalidOperationException("IP2Region未初始化，请先调用SetWebHostEnvironment方法");
        }

        if (string.IsNullOrEmpty(ipAddress))
            return "Unknown Location";

        // 检查是否是本地回环地址
        if (ipAddress == "::1" || ipAddress == "127.0.0.1")
            return "本地(Local network)";

        // 检查IPv4和IPv6格式
        if (!_ipv4Regex.IsMatch(ipAddress) && !_ipv6Regex.IsMatch(ipAddress))
            return "Unknown Location";

        try
        {
            // 使用 Task.Run 包装同步调用以保持异步接口
            var result = await Task.Run(() => _searcher.Search(ipAddress));
            return result ?? "Unknown Location";
        }
        catch
        {
            return "Unknown Location";
        }
    }

    /// <summary>
    /// 判断是否是内网IP
    /// </summary>
    private static bool IsInternalIp(IPAddress ip)
    {
        if (IPAddress.IsLoopback(ip)) return true;
        
        byte[] bytes = ip.GetAddressBytes();
        return bytes[0] switch
        {
            10 => true,
            172 => bytes[1] >= 16 && bytes[1] <= 31,
            192 => bytes[1] == 168,
            _ => false
        };
    }

    #endregion

    #region 客户端IP获取相关

    /// <summary>
    /// 获取客户端IP地址
    /// </summary>
    /// <param name="httpContext">HTTP上下文</param>
    /// <param name="defaultIp">默认IP地址，当无法获取时返回</param>
    /// <returns>客户端IP地址</returns>
    /// <remarks>
    /// 支持多种环境下的IP获取：
    /// 1. 代理服务器环境（X-Forwarded-For, X-Real-IP）
    /// 2. 直连环境（RemoteIpAddress）
    /// 3. 自动处理IPv4和IPv6地址
    /// 4. 智能处理本地回环地址
    /// </remarks>
    public static string GetClientIpAddress(HttpContext? httpContext, string defaultIp = "127.0.0.1")
    {
        if (httpContext == null)
        {
            return defaultIp;
        }

        try
        {
            // 优先从X-Forwarded-For获取（代理服务器环境）
            var forwardedFor = httpContext.Request.Headers["X-Forwarded-For"].ToString();
            if (!string.IsNullOrEmpty(forwardedFor))
            {
                // 取第一个IP（最原始的客户端IP）
                var ips = forwardedFor.Split(',', StringSplitOptions.RemoveEmptyEntries);
                if (ips.Length > 0)
                {
                    var ip = ips[0].Trim();
                    return ProcessClientIpAddress(ip, defaultIp);
                }
            }

            // 其次从X-Real-IP获取（Nginx等反向代理）
            var realIp = httpContext.Request.Headers["X-Real-IP"].ToString();
            if (!string.IsNullOrEmpty(realIp))
            {
                return ProcessClientIpAddress(realIp, defaultIp);
            }

            // 最后从RemoteIpAddress获取（直连环境）
            var remoteIp = httpContext.Connection.RemoteIpAddress?.ToString();
            if (!string.IsNullOrEmpty(remoteIp))
            {
                return ProcessClientIpAddress(remoteIp, defaultIp);
            }

            // 如果都无法获取，尝试获取本机内网IP
            var localIp = GetPreferredLocalIpAddress();
            return !string.IsNullOrEmpty(localIp) ? localIp : defaultIp;
        }
        catch
        {
            return defaultIp;
        }
    }

    /// <summary>
    /// 处理客户端IP地址
    /// </summary>
    /// <param name="ip">原始IP地址</param>
    /// <param name="defaultIp">默认IP地址</param>
    /// <returns>处理后的IP地址</returns>
    private static string ProcessClientIpAddress(string ip, string defaultIp)
    {
        if (string.IsNullOrEmpty(ip))
        {
            return defaultIp;
        }

        // 处理IPv6回环地址
        if (ip == "::1")
        {
            // 尝试获取本机内网IP
            var localIp = GetPreferredLocalIpAddress();
            return !string.IsNullOrEmpty(localIp) ? localIp : "127.0.0.1";
        }

        // 处理IPv4回环地址
        if (ip == "127.0.0.1")
        {
            // 尝试获取本机内网IP
            var localIp = GetPreferredLocalIpAddress();
            return !string.IsNullOrEmpty(localIp) ? localIp : ip;
        }

        return ip;
    }

    #endregion

    #region 本机IP获取相关

    /// <summary>
    /// 获取本机内网IP地址列表
    /// </summary>
    /// <returns>内网IP地址列表</returns>
    public static List<string> GetLocalIpAddresses()
    {
        var localIps = new List<string>();
        
        try
        {
            // 获取所有网络接口
            var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
            
            foreach (var networkInterface in networkInterfaces)
            {
                // 只处理活跃的以太网和无线网卡
                if (networkInterface.OperationalStatus != OperationalStatus.Up ||
                    (networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet &&
                     networkInterface.NetworkInterfaceType != NetworkInterfaceType.Wireless80211))
                {
                    continue;
                }
                
                // 获取IP地址信息
                var ipProperties = networkInterface.GetIPProperties();
                foreach (var unicastAddress in ipProperties.UnicastAddresses)
                {
                    var ip = unicastAddress.Address;
                    
                    // 只处理IPv4地址
                    if (ip.AddressFamily != AddressFamily.InterNetwork)
                        continue;
                    
                    // 过滤掉回环地址
                    if (IPAddress.IsLoopback(ip))
                        continue;
                    
                    // 检查是否是内网地址
                    if (IsInternalIp(ip))
                    {
                        localIps.Add(ip.ToString());
                    }
                }
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"获取本机内网IP失败: {ex.Message}");
        }
        
        return localIps;
    }

    /// <summary>
    /// 获取首选的本机内网IP地址
    /// </summary>
    /// <returns>首选的内网IP地址，如果没有则返回空字符串</returns>
    public static string GetPreferredLocalIpAddress()
    {
        var localIps = GetLocalIpAddresses();
        
        if (localIps.Count == 0)
            return string.Empty;
        
        // 优先选择192.168.x.x网段的IP
        var preferredIp = localIps.FirstOrDefault(ip => ip.StartsWith("192.168."));
        if (!string.IsNullOrEmpty(preferredIp))
            return preferredIp;
        
        // 其次选择10.x.x.x网段的IP
        preferredIp = localIps.FirstOrDefault(ip => ip.StartsWith("10."));
        if (!string.IsNullOrEmpty(preferredIp))
            return preferredIp;
        
        // 最后选择172.16-31.x.x网段的IP
        preferredIp = localIps.FirstOrDefault(ip => ip.StartsWith("172."));
        if (!string.IsNullOrEmpty(preferredIp))
            return preferredIp;
        
        // 如果都没有，返回第一个
        return localIps.First();
    }

    /// <summary>
    /// 获取本机主机名
    /// </summary>
    /// <returns>主机名</returns>
    public static string GetHostName()
    {
        try
        {
            return Dns.GetHostName();
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"获取主机名失败: {ex.Message}");
            return "Unknown";
        }
    }

    /// <summary>
    /// 获取本机网络信息
    /// </summary>
    /// <returns>网络信息字典</returns>
    public static Dictionary<string, object> GetLocalNetworkInfo()
    {
        var networkInfo = new Dictionary<string, object>
        {
            ["HostName"] = GetHostName(),
            ["LocalIpAddresses"] = GetLocalIpAddresses(),
            ["PreferredLocalIp"] = GetPreferredLocalIpAddress()
        };
        
        return networkInfo;
    }

    #endregion
}
